#ifndef CSVWRITER_HPP_
#define CSVWRITER_HPP_

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>

#include "Logger.hpp"
#include "CsvReader.hpp"
#include "Mapper.hpp"

/*
 * Reads ints from tmp-file and stores them as CSV in output-file
 */
class CsvWriter {
private:
	File rFile, wFile;

	unsigned int bufSize, bufOffset;
	char buf[50];
	bool bufIsEmpty;
public:
	CsvWriter(char *path) {
		assert(0 == chunkSize % pageSize);
		wFile.setPath(path);
		wFile.Open(File::MODE_RW);

		bufSize = 0;
		bufOffset = 0;
		bufIsEmpty = 1;
	}

	void setFile(char *tmpPath) {
		rFile.setPath(tmpPath);
		rFile.Open(File::MODE_READ);
	}
	
	~CsvWriter() {
		if (rFile.isMapped) {
			Mapper::unmap(rFile);
		}
		if (rFile.isOpened) {
			rFile.Close();
		}
		if (unlink(rFile.path) != 0) {
			Logger::getInstance().log(Logger::WARNING, "Failed to remove file *%s*.\n", rFile.path);		
		}
		if (wFile.isMapped) {
			Mapper::unmap(wFile);
		}
		if (wFile.isOpened) {
			wFile.Close();
		}
	}

	void writeResult() {
		assert(rFile.isOpened);
		assert(wFile.isOpened);

		while (rFile.cSize < rFile.size) {
			if (rFile.isMapped) {
				Mapper::unmap(rFile);
			}
			rFile.mSize = std::min(chunkSize, rFile.size - rFile.offset);//
			Mapper::map(rFile, Mapper::MODE_READ);

			assert(0 == rFile.mSize % sizeof(int));
			for (unsigned int i = 0; i < rFile.mSize/sizeof(int); ++i) {
				assert(1 == bufIsEmpty);
				assert(0 == bufSize);
				assert(0 == bufOffset);
				sprintf(buf, "%d", *(int*)rFile.cPtr);
				strncat(buf, ",", 1);
				// "\n" may be inserted to make out-file more readable. This piece of code can be added/removed.
				//if (i % 20 == 19) {
				//	strncat(buf, "\n", 1);
				//}
				//
				bufSize = strlen(buf);
				bufIsEmpty = 0;
				bufOffset = 0;
				
				if (wFile.offset - wFile.cSize < bufSize) {
					if (wFile.offset - wFile.cSize > 0) {
						strncpy(wFile.cPtr,buf,wFile.offset - wFile.cSize);
						wFile.cPtr += wFile.offset - wFile.cSize;
						wFile.cSize += wFile.offset - wFile.cSize;
					}
					bufSize -= wFile.offset - wFile.cSize;
					bufOffset = wFile.offset - wFile.cSize;

					if (wFile.isMapped) {
						Mapper::unmap(wFile);
					}
					wFile.mSize = chunkSize;
					Mapper::map(wFile, Mapper::MODE_RW);
					bufIsEmpty = 0;
				}
				strncpy(wFile.cPtr,&(buf[bufOffset]),bufSize);
				wFile.cPtr += bufSize;
				wFile.cSize += bufSize;

				bufSize = 0;
				bufOffset = 0;
				bufIsEmpty = 1;

				rFile.cPtr += 4;
				rFile.cSize += sizeof(int);
			}
		  
		}
		
		if (wFile.isMapped) {
			Mapper::unmap(wFile);
		}

		if (-1 == ftruncate(wFile.fd,wFile.cSize)) {	
			Logger::getInstance().log(Logger::ERROR, "ftruncate of file *%s* failed.\n", wFile.path);		
			exit(1);
		}
	}
};

#endif /* CSVWRITER_HPP_ */
